package com.roadmap.client.msg;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Vector;

import org.apache.log4j.Logger;
import org.hibernate.Hibernate;

import com.roadmap.client.module.BaseManager;
import com.roadmap.common.BaseConst;
import com.roadmap.common.BaseEntity;
import com.roadmap.common.RdcLogger;
import com.roadmap.entities.Dialog;
import com.roadmap.entities.Message;
import com.roadmap.entities.ext.DialogExt;
import com.roadmap.entities.ext.MessageExt;
import com.roadmap.exception.ActionNotSupportedException;
import com.roadmap.exception.EntityNotFoundException;
import com.roadmap.exception.InvalidActionException;
import com.roadmap.exception.InvalidQueryException;
import com.roadmap.exception.InvalidStatusException;
import com.roadmap.exception.InvalidValueException;
import com.roadmap.exception.NullObjectException;
import com.roadmap.exception.ServerException;
import com.roadmap.repo.db.RdcQuery;

public class MessageManager extends BaseManager {
	private final static Logger _log = RdcLogger
			.getLogger(MessageManager.class);

	public MessageManager(String parentId) {
		super(parentId, Message.class);
	}

	/**
	 * create the same message to one more people. when the user sends a new
	 * message that means a new dialog is started. the new message is the first
	 * one of the dialog.
	 * 
	 * @throws InvalidValueException
	 */
	public void create(String[] recipients, BaseEntity template)
			throws ServerException, InvalidActionException,
			InvalidValueException {
		if (recipients == null || recipients.length == 0) {
			NullObjectException e = new NullObjectException(
					"recipients or template is invalid.");
			_log.error(e);
			throw e;
		}
		if (!checkEntityIsValid(template)) {
			InvalidValueException e = new InvalidValueException(
					"invalid template message");
			_log.error(e);
			throw e;
		}

		Message temp = (Message) template;
		// create new dialogs firstly
		Vector<BaseEntity> dialogs = new Vector<BaseEntity>();
		for (int i = 0; i < recipients.length; i++) {
			// check if the id is valid
			if (recipients[i] == null
					|| recipients[i].length() != BaseConst.DATABASE_TABLE_ID_LENGTH) {
				continue;
			}
			Dialog dlg = new Dialog();
			dlg.setSponsorId(getOwnerId());
			dlg.setParticipatorId(recipients[i]);
			dialogs.add(dlg);
		}
		// firstly save the dialogs
		if (!dialogs.isEmpty()) {
			getDbUtil().saveOrUpdateEntities(dialogs);
		}
		else {
			_log.warn("failed to create new dialogs.");
			return;
		}

		// create new messages
		Vector<BaseEntity> messages = new Vector<BaseEntity>();
		for (int i = 0; i < dialogs.size(); i++) {
			Message msg = (i == 0) ? temp : temp.clone();
			msg.setDispatcherId(getOwnerId());
			Dialog dlg = (Dialog) dialogs.get(i);
			msg.setDialogId(dlg.getId());
			messages.add(msg);
		}
		if (!messages.isEmpty()) {
			getDbUtil().saveOrUpdateEntities(messages);
		}
		else {
			_log.warn("failed to create new messages.");
		}
	}

	/** get the messages sent by the user */
	@Override
	public List getEntitiesOfAspirant() throws ServerException {
		String sql = "select {m.*}, m.aspirantId, m.realName from (select msg.*, d.aspirantId, d.realName from message as msg, (select dlg.dialogId, a.aspirantId, a.realName from dialog as dlg, aspirant as a where (dlg.sponsorId=:me and dlg.participatorId=a.aspirantId) || (dlg.participatorId=:me and dlg.sponsorId=a.aspirantId)) as d where msg.dialogId=d.dialogId and msg.dispatcherId=:me order by msg.createdTime DESC) as m group by m.dialogId having m.createdTime<=max(m.createdTime) order by m.createdTime DESC";
		RdcQuery query = getDbUtil().createNativeSQLQuery(sql);
		query.addEntity("m", Message.class);
		query.setParameter("me", getOwnerId());
		query.addScalar("aspirantId", Hibernate.STRING);
		query.addScalar("realName", Hibernate.STRING);
		query.setMaxResults(BaseConst.MESSAGE_MAX_RESULTS);
		List results = query.getResults();
		List<MessageExt> messages = null;
		if (results != null && !results.isEmpty()) {
			messages = new ArrayList<MessageExt>();
			Iterator iter = results.iterator();
			while (iter.hasNext()) {
				Object[] result = (Object[]) iter.next();
				MessageExt msg = new MessageExt((Message) result[0]);
				msg.setRecipientId((String) result[1]);
				msg.setRecipientName((String) result[2]);
				messages.add(msg);
			}
		}
		return messages;
	}

	/** get the messages sent to the user */
	@Override
	public List<MessageExt> getExecutableEntities()
			throws ActionNotSupportedException, InvalidQueryException,
			InvalidValueException, ServerException {
		String sql = "select {m.*}, m.aspirantId, m.realName from (select msg.*, d.aspirantId, d.realName from message as msg, (select dlg.dialogId, a.aspirantId, a.realName from dialog as dlg, aspirant as a where (dlg.sponsorId=:me and dlg.participatorId=a.aspirantId) || (dlg.participatorId=:me and dlg.sponsorId=a.aspirantId)) as d where msg.dialogId=d.dialogId and msg.dispatcherId=d.aspirantId order by msg.createdTime DESC) as m group by m.dialogId order by m.createdTime DESC";
		RdcQuery query = getDbUtil().createNativeSQLQuery(sql);
		query.addEntity("m", Message.class);
		query.setParameter("me", getOwnerId());
		query.addScalar("aspirantId", Hibernate.STRING);
		query.addScalar("realName", Hibernate.STRING);
		query.setMaxResults(BaseConst.MESSAGE_MAX_RESULTS);
		List results = query.getResults();
		List<MessageExt> messages = null;
		Vector<BaseEntity> msgVec = null;
		if (results != null && !results.isEmpty()) {
			messages = new ArrayList<MessageExt>();
			msgVec = new Vector<BaseEntity>();
			Iterator iter = results.iterator();
			while (iter.hasNext()) {
				Object[] result = (Object[]) iter.next();
				Message m = (Message) result[0];
				// create an extended message
				MessageExt msg = new MessageExt(m);
				msg.setDispatcherId((String) result[1]);
				msg.setDispatcherName((String) result[2]);
				messages.add(msg);

				// change the message's status
				if (m.getStatus() == BaseConst.MESSAGE_STATUS_UNREAD) {
					m.setStatus(BaseConst.MESSAGE_STATUS_READ);
					msgVec.add(m);
				}
			}
		}

		// change the new messages to READ
		if (msgVec != null && !msgVec.isEmpty()) {
			getDbUtil().saveOrUpdateEntities(msgVec);
		}
		return messages;
	}

	/** get the latest message specified by the dialog id */
	@Override
	public BaseEntity getEntityExt(String entityId) throws NullObjectException,
			InvalidQueryException, ServerException, InvalidValueException {
		String sql = "select {m.*}, a.realName from message as m, aspirant as a where m.dialogId=:dlgid and m.dispatcherId=a.aspirantId order by m.createdTime ASC";
		RdcQuery query = getDbUtil().createNativeSQLQuery(sql);
		query.addEntity("m", Message.class);
		query.setParameter("dlgid", entityId);
		query.addScalar("realName", Hibernate.STRING);
		List results = query.getResults();
		if (results == null || results.isEmpty()) {
			return null;
		}

		List<MessageExt> messages = new ArrayList<MessageExt>();
		Vector<BaseEntity> msgVec = null;
		Iterator iter = results.iterator();
		while (iter.hasNext()) {
			Object[] result = (Object[]) iter.next();
			Message m = (Message) result[0];
			MessageExt msg = new MessageExt(m);
			msg.setDispatcherName((String) result[1]);
			messages.add(msg);

			// change the message's status after the recipient loads the new message
			if (msg.getStatus() == BaseConst.MESSAGE_STATUS_UNREAD
					&& !m.getDispatcherId().equals(getOwnerId())) {
				if (msgVec == null) {
					msgVec = new Vector<BaseEntity>();
				}
				m.setStatus(BaseConst.MESSAGE_STATUS_READ);
				msgVec.add(m);
			}
		}

		// get the dialog people's names
		sql = "select {d.*}, a.aspirantId, a.realName from dialog as d, aspirant as a where d.dialogId=:dlgid and (d.sponsorId=a.aspirantId || d.participatorId=a.aspirantId)";
		query = getDbUtil().createNativeSQLQuery(sql);
		query.addEntity("d", Dialog.class);
		query.setParameter("dlgid", entityId);
		query.addScalar("aspirantId", Hibernate.STRING);
		query.addScalar("realName", Hibernate.STRING);
		results = query.getResults();
		// the size of returned results should be two.
		DialogExt dialog = null;
		if (results != null && results.size() == 2) {
			iter = results.iterator();
			while (iter.hasNext()) {
				Object[] result = (Object[]) iter.next();
				if (dialog == null) {
					dialog = new DialogExt((Dialog) result[0]);
				}
				// assign the right names
				if (dialog.getSponsorId().equals((String) result[1])) {
					dialog.setSponsorName((String) result[2]);
				}
				else {
					dialog.setParticipatorName((String) result[2]);
				}
			}
		}

		// set the relating messages.
		dialog.setMessages(messages);

		// update the message's status in database
		if (msgVec != null && !msgVec.isEmpty()) {
			getDbUtil().saveOrUpdateEntities(msgVec);
		}

		return dialog;
	}

}
